﻿using System.Threading.Tasks;
using SDCloudSolution.SDCloud.ModelRunAgent.Infrastructure.Interfaces;
using SDCloudSolution.SDCloud.Entities.Models.Data;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Data.Entity.Core.Metadata.Edm;
using System.Linq;
using SDCloudSolution.SDCloud.ModelRunAgent.Common;
using SDCloudSolution.SDCloud.ServiceAgent;

namespace SDCloudSolution.SDCloud.ModelRunAgent.Infrastructure
{
    public class PysdModelExecutor : BaseModelExecutor
    {
        private ServiceManager pyServiceManager;

        private const string RunVensimModel = "/api/pysd/vensim/"; //<path:modelPath>
        private const string RunXmileModel = "/api/pysd/xmile/"; //<path:modelPath>

        public PysdModelExecutor(IModelExecutionContextProvider contextProvider) 
            : base(contextProvider)
        {
            pyServiceManager = new ServiceManager(
                AppConfigHelper.PythonPath,
                AppConfigHelper.PysdArguments,
                AppConfigHelper.PyServiceUrl,
                AppConfigHelper.PyServicePortNumbers,
                AppConfigHelper.PyServiceTimeOut
                );
        }

        private static T GetValue<T>(JToken jToken, string key, T defaultValue = default(T))
        {
            dynamic ret = jToken[key];
            if (ret == null) return defaultValue;
            if (ret is JObject) return JsonConvert.DeserializeObject<T>(ret.ToString());
            return (T)ret;
        }

        private static Dictionary<string, double> GetValues(string[] columnNames, double[] values)
        {
            var result = new Dictionary<string, double>();
            var index = 1;
            foreach (var columnName in columnNames.Skip(1))
            {
                result.Add(columnName, values.ElementAt(index++));
            }
            return result;
        }

        private ModelResultDataEntity ConvertToModelResultEntity(string jsonModel)
        {
            var result = new ModelResultDataEntity();

            JObject model = JObject.Parse(jsonModel);
            var columnNames = model.Properties().Select(property => property.Name).ToArray();
            var allColumns = columnNames.Select(name => new ModelResultColumnEntity
            {
                Name = name
            }).ToArray();
            result.Columns = allColumns;

            var timeFrames = new List<ModelResultTimeFrameEntity>();

            var fullList = new List<List<double>>();
            foreach (JToken child in model.Children())
            {
                // read all json values
                foreach (JToken child_child in child.Children())
                {
                    var columnValuesList = new List<double>();
                    for (var i = 0; i < child.Values().Count(); i++)
                    {
                        columnValuesList.Add(GetValue<double>(child_child, i.ToString(), 0));
                    }
                    fullList.Add(columnValuesList);
                }
            }

            if (!fullList.Any()) return result;
            double initialTime = 0;
            for (var i = 0; i < fullList.First().Count; i++)
            {
                var currentTime = fullList.First().ElementAt(i);
                var valuesArray = fullList.Select(list => list.ElementAt(i)).ToArray();
                var timeFrame = new ModelResultTimeFrameEntity
                {
                    Time = currentTime,
                    TimeDelta = currentTime - initialTime,
                    Values = GetValues(columnNames, valuesArray)
                };
                initialTime = currentTime;
                timeFrames.Add(timeFrame);
            }

            result.TimeFrames = timeFrames.ToArray();
            return result;
        }

        public override Task<ModelResultDataEntity> ExecuteModel(string modelFullFilePath)
        {
            return
                pyServiceManager
                    .GetResponse($"{RunVensimModel}{modelFullFilePath}")
                     .ContinueWith(data => ConvertToModelResultEntity(data.Result));
        }
    }
}
